package com.apache.netty.codec;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;

public class NettyServer {
  public static void main(String[] args) throws Exception {

    // 创建BossGroup 和 WorkerGroup
    // 说明
    // 1. 创建两个线程组 bossGroup 和 workerGroup
    // 2. bossGroup 只是处理连接请求 , 真正的和客户端业务处理，会交给 workerGroup完成
    // 3. 两个都是无限循环
    // 4. bossGroup 和 workerGroup 含有的子线程(NioEventLoop)的个数
    //   默认实际 cpu核数 * 2
    EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    EventLoopGroup workerGroup = new NioEventLoopGroup(); // 8

    try {
      // 创建服务器端的启动对象，配置参数
      ServerBootstrap bootstrap = new ServerBootstrap();

      // 使用链式编程来进行设置
      bootstrap
          .group(bossGroup, workerGroup) // 设置两个线程组
          .channel(NioServerSocketChannel.class) // 使用NioSocketChannel 作为服务器的通道实现
          .option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列得到连接个数
          .childOption(ChannelOption.SO_KEEPALIVE, true) // 设置保持活动连接状态
          //                    .handler(null) // 该 handler对应 bossGroup , childHandler 对应
          // workerGroup
          .childHandler(
              new ChannelInitializer<SocketChannel>() { // 创建一个通道初始化对象(匿名对象)
                // 给pipeline 设置处理器
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {

                  ChannelPipeline pipeline = ch.pipeline();
                  // 在pipeline加入ProtoBufDecoder
                  // 指定对哪种对象进行解码
                  pipeline.addLast(
                      "decoder", new ProtobufDecoder(StudentPOJO.Student.getDefaultInstance()));
                  pipeline.addLast(new NettyServerHandler());
                }
              }); // 给我们的workerGroup 的 EventLoop 对应的管道设置处理器

      System.out.println(".....服务器 is ready...");

      // 绑定一个端口并且同步, 生成了一个 ChannelFuture 对象
      // 启动服务器(并绑定端口)
      ChannelFuture cf = bootstrap.bind(6668).sync();

      // 给cf 注册监听器，监控我们关心的事件

      cf.addListener(
          new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
              if (cf.isSuccess()) {
                System.out.println("监听端口 6668 成功");
              } else {
                System.out.println("监听端口 6668 失败");
              }
            }
          });

      // 对关闭通道进行监听
      cf.channel().closeFuture().sync();
    } finally {
      bossGroup.shutdownGracefully();
      workerGroup.shutdownGracefully();
    }
  }
}
